/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*!
 * @file
 * @brief	複数の Component に関するヘッダーファイル
 *
 * このファイルは複数の Component に関するヘッダーファイルです。
 *
 * @author	Shun Moriya <shun@mnu.sakura.ne.jp>
 */

#if !defined(___GRAPH2D_CONTAINER_H___)
#define ___GRAPH2D_CONTAINER_H___

#include "component.h"

namespace Graph2D
{
	/*!
	 * @class
	 * @brief	複数の Component を管理するクラスです
	 *
	 * Component を配列に登録し管理します。配列の番号が低いほど優先的に描画されます。
	 */
	class Container : public Component
	{
		DISALLOW_COPY_AND_ASSIGN(Container);

		typedef Component super;

#define GRAPH2D_CONTAINER_FLAG_FRAME_TEXTURE_REPEAT_H			(0x00000001)
#define GRAPH2D_CONTAINER_FLAG_FRAME_TEXTURE_REPEAT_V			(0x00000002)
#define GRAPH2D_CONTAINER_FLAG_COLOR_AFFECTED					(0x20000000)
#define GRAPH2D_CONTAINER_FLAG_SCROLLABLE						(0x40000000)
#define GRAPH2D_CONTAINER_FLAG_PERSPECTIVE_MODE					(0x80000000)

	public:
		typedef enum Direction
		{
			DIRECTION_CENTER = 0,
			DIRECTION_NORTH,
			DIRECTION_SOUTH,
			DIRECTION_EAST,
			DIRECTION_WEST,
			DIRECTION_NORTH_EAST,
			DIRECTION_NORTH_WEST,
			DIRECTION_SOUTH_EAST,
			DIRECTION_SOUTH_WEST,
		}Direction;

	public:
		Container();
		virtual ~Container();

		virtual const char* getTypeName() const
		{
			return "Container";
		}

		Vector2 getClientSize() const
		{
			return Vector2(
				getSize().x - (leftFrameSize + rightFrameSize),
				getSize().y - (topFrameSize + bottomFrameSize)
			);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// container (pure virtual function)
		virtual size_t getCount() const = 0;
		virtual Component* get(const size_t index) const = 0;
		virtual void set(const size_t index, Component* component) = 0;
		virtual void swap(const size_t to, const size_t from) = 0;
		virtual void clear();
		void remove(Component* component);
		bool find(Component* component) const;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// visibility
		virtual void open();
		virtual void close();
		virtual bool closed() const;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// mode
		bool getPerspectiveMode() const;
		void setPerspectiveMode(const bool enable);
		void affecte(const bool enable);
		bool getScrollable() const;
		void setScrollable(const bool scrollable);

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// scroll
		const Vector2& getScrollPosition() const;
		void setScrollPosition(const Vector2& localPosition, const bool immediately = true);
		void setScrollPosition(const float x, const float y, const bool immediately = true);
		const Vector2& getScrollSize() const;
		void setScrollSize(const Vector2& size);
		void setScrollSize(const float w, const float h);

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// frame
		//! テクスチャを読み込みます
		bool loadFrameTexture(const Direction direction, const std::string& filename);
		void unloadFrameTexture(const Direction direction);

		//! 四隅フレームテクスチャUV座標
		const Vector2& getFrameTexCoord(const Direction direction) const;
		void setFrameTexCoord(const Direction direction, const Vector2& frameTexCoord);
		void setFrameTexCoord(const Direction direction, const float u, const float v);

		//! 四隅フレームテクスチャサイズ
		float getLeftFrameSize() const;
		void setLeftFrameSize(const float size);
		float getTopFrameSize() const;
		void setTopFrameSize(const float size);
		float getRightFrameSize() const;
		void setRightFrameSize(const float size);
		float getBottomFrameSize() const;
		void setBottomFrameSize(const float size);

		void fit();

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// touch event
		virtual bool touchesBegan(const Vector2& localPosition);
		virtual bool touchesMoved(const Vector2& localPosition);
		virtual bool touchesEnded(const Vector2& localPosition);
		virtual bool touchesCancelled(const Vector2& localPosition);

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// event
		virtual void onSerialize(mana_stream* stream) const;
		virtual void onDeserialize(mana_stream* stream);
		virtual void onUpdate(const UpdateInfomation& updateInfomation);
		virtual void onUpdateLayout();
		virtual bool onDraw(const DrawRect& drawRect);
		virtual bool onTouchesBegan(const Vector2& localPosition);
		virtual bool onTouchesEnded(const Vector2& localPosition);
		virtual bool onTouchesMoved(const Vector2& localPosition);
		virtual bool onTouchesCancelled(const Vector2& localPosition);
		virtual bool onSize(const Vector2& size);

	private:
		void correctScrollTargetOffset();
		void shellSort(Component** array, const unsigned int length) const;

	protected:
		void drawChildren(const DrawRect& drawRect, Component** array, const unsigned int length);
		void drawFrame(const DrawRect& drawRect);
		void drawFrameParts(const Direction direction, const Vector2& v0, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Color& color, const float width, const float height);
		void drawChild(const DrawRect& drawRect, Component* component);

	private:
		unsigned int flags;					//!< フラグ

		Texture* frameTexture[DIRECTION_SOUTH_WEST + 1];	//!< フレームテクスチャ
		Vector2 frameTexCoord[DIRECTION_SOUTH_WEST + 1];	//!< フレームテクスチャUV座標
		float leftFrameSize;				//!< 左側フレームサイズ
		float topFrameSize;					//!< 上側フレームサイズ
		float rightFrameSize;				//!< 右側フレームサイズ
		float bottomFrameSize;				//!< 下側フレームサイズ

		Vector2 toucheBeganScrollOffset;	//!< タッチ開始時のスクロール値
		Vector2 scrollOffsetLimit;			//!< スクロール限界値
		Vector2 scrollOffset;				//!< スクロール値
		Vector2 scrollTargetOffset;			//!< スクロールターゲット値
	};
}

#endif
